include Enum

class GameController < ApplicationController

  before_filter :confirm_logged_in
  before_filter :restore_state,    :except => [ :new ]

  def new
    @game = find_game
    unless @game.nil?
      @game.destroy
    end
    create_game
  end

  def index
    if game_status_code == Enum::PLAY
      @gamer.mark_cards(@table.source)
    else
      @state = 'game_over'
      @hint_message = t([:draw, :you_win, :you_defeat].at(game_status_code))
    end
  end

  def move
    card = @gamer.check_is_valid_answer(params[:card], @table.source)
    unless card.nil?
      push_card_on_the_table(card, @gamer)

      if (@gamer.isAttacking)
        unless (@state =='go_on' && game_status_code == Enum::PLAY)
          attacking(card)
        end
      else
        defending(card)
      end

      saveState
    end
  end


  def rebound
    invalidateAttacking(true)
    card = @opponent.get_card_with_min_value
    push_card_on_the_table(card, @opponent)
    @gamer.mark_cards(@table.source)
    store_hint(:opponent_moving, :name => @opponent.name, :card => card.to_s)
    @state = 'get_all'

    saveState
  end

  def get_all
    @gamer.putAll(@table.receive_all)

    if (game_status_code == Enum::PLAY)
      invalidateAttacking()
      #auto answer
      card = @opponent.get_card_with_min_value
      push_card_on_the_table(card, @opponent)
      store_hint(:opponent_moving, :name => @opponent.name, :card => card.to_s)
      @gamer.mark_cards(@table.source)
      @state = 'get_all'
    else
      @state = 'game_over'
    end

    saveState
  end


  def goOn
    @state = ''
    @opponent.putAll(@table.receive_all)
    store_hint(:opponent_got_card, :name => @opponent.name)
    invalidateAttacking()

    saveState
  end


  private

  def find_game
    user_id = session[:user_id]
    games = Game.find_by_user_id(user_id)
  end

  def create_game
    user_id = session[:user_id]
    username = User.find(user_id)

    @deck = Factory.create(:deck)
    @table = Factory.create(:table)
    @gamer = Factory.create(:gamer)
    @opponent = Factory.create(:opponent)

    #hand_out
    @deck.hand_out()

    invalidateAttacking(false)
    store_hint(:you_move_first, :name => username.name)

    @game = Game.new(:user_id => user_id,
                     :deck => @deck.serialize,
                     :table => @table.serialize,
                     :gamer => @gamer.serialize,
                     :opponent => @opponent.serialize,
                     :trump => @deck.trump.serialize,
                     :user_is_attacking => @gamer.isAttacking,
                     :hint_message_keys => @hint_keys
    )

    if @game.save
      redirect_to(:action => 'index')
    end
  end

  #convert locale messages to string for next store in db
  def store_hint(*args)
    @hint_keys = args.to_json
    #TODO: conver card to string
    logger.warn(t(args))
  end

  def parse_locale_keys(text)
    begin
      # .. process
      hash = JSON.parse(text)
      options  = hash.last.is_a?(Hash) ? hash.pop : {}
      key      = hash.shift

      options.symbolize_keys!
      result = t(key, options)
    rescue
      # .. handle error
      result = ''
    ensure
      return result
    end

  end

  def restore_state
    @game = find_game
    if @game.nil?
      create_game()
    end

    @trump = Card.new(0, 0)
    @trump.deserialize(@game.trump)

    @deck = Factory.create(:deck)
    @table = Factory.create(:table)
    @gamer = Factory.create(:gamer)
    @opponent = Factory.create(:opponent)

    items_map = {:deck => @deck, :table => @table, :gamer => @gamer, :opponent => @opponent}
    items_map.each { |key, value|
      #deserialize
      value.deserialize(@game[key])
      #evaluate
      value.source.each { |card| card.isTrump = card.suit == @trump.suit }
      #sort user's cards
      value.sortCards() if (key == :gamer)
    }

    @gamer.isAttacking = @game.user_is_attacking
    @state = @game.state
    @hint_message = parse_locale_keys( @game.hint_message_keys )
  end

  def saveState
    @game = find_game
    # Update the object
    @game.update_attributes(:deck => @deck.serialize,
                            :table => @table.serialize,
                            :gamer => @gamer.serialize,
                            :opponent => @opponent.serialize,
                            :user_is_attacking => @gamer.isAttacking,
                            :state => @state,
                            :hint_message_keys => @hint_keys || ''
    )

    # If update succeeds or fail, index
    redirect_to(:action => 'index')
  end


  def invalidateAttacking(toggle_player = false)
    @state = ''
    @table.clear
    rechargeCards()

    if (toggle_player)
      @gamer.isAttacking = !@gamer.isAttacking
      store_hint(:your_move, :name => @opponent.name) if @gamer.isAttacking
    end

  end

  # refresh cards balance
  def rechargeCards
    queue = [@gamer, @opponent]
    queue.reverse! if !@gamer.isAttacking

    queue.each { |player|
      if player.necessary_cards_count > 0
        cards = @deck.getCards(player.necessary_cards_count)
        player.receive(cards)
      end
      player.reset_highlights
    }
  end

  def push_card_on_the_table(card, player)
    player.get_card(card)
    @table.put(card)
  end


  #Define attacking
  def attacking(card)
    store_hint(:you_attacking, :card => card.to_s)
    responseCard = @opponent.decide_defending(card)
    if game_status_code != Enum::PLAY
      #Exit
      return
    end
    if (responseCard)
      @state = 'rebound'
      push_card_on_the_table(responseCard, @opponent)
      store_hint(:opponent_defending, :name => @opponent.name, :card => responseCard.to_s)
    else
      @state = 'go_on'
      #todo: analise card for attacking. and if not card for moving - not show about throw
      store_hint(:opponent_getting_card, :name => @opponent.name, :card => responseCard.to_s)
    end

    @state = 'game_over' if (game_status_code != Enum::PLAY)
  end

  def defending(responseCard)
    store_hint(:you_defending, :card => responseCard.to_s)
    attackingCard = @opponent.decide_attack(@table.source, @deck.empty?)
    if (attackingCard)
      store_hint(:opponent_moving, :name => @opponent.name, :card => attackingCard.to_s)
      push_card_on_the_table(attackingCard, @opponent)
    else
      #auto toggle
      invalidateAttacking(true)
      @state = ''
      #store_hint(:rebound_action)

      @state = 'game_over' if (game_status_code != Enum::PLAY)
    end
  end

  #handle finish of the game
  def game_status_code
    resultCode = -1
    if (@deck.empty? && (@gamer.empty? || @opponent.empty?))
      if (@gamer.empty? && @opponent.empty?)
        resultCode = DRAW
      elsif (@gamer.empty?)
        resultCode = USER_WIN
      else
        resultCode = USER_DEFEAT
      end
      @state = 'game_over'
    end

    return resultCode
  end

end